﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Realmd/Net/AuthSocketHandler.h"
#include <cstdio>
#include "Shared/Binary/LocalByteBuffer.h"
#include "Shared/Log/Log.h"
#include "Shared/Util/Application.h"
#include "Realmd/Global.h"
#include "Realmd/AuthCode.h"
using namespace Shared;
using namespace Shared::Net;
using namespace Shared::Crypto;
using namespace Shared::DB;

typedef Shared::Binary::LocalByteBuffer<0x1000> LocalByteBuffer;

enum eStatus
{
    STATUS_CONNECTED = 0,
    STATUS_AUTHED
};

typedef struct AuthHandler
{
    eAuthCmd cmd;
    std::uint32_t status;
    bool (AuthSocketHandler::*handler)(void);
} AuthHandler;

const AuthHandler table[] =
{
    { CMD_AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, &AuthSocketHandler::handleLogonChallenge },
    { CMD_AUTH_LOGON_PROOF, STATUS_CONNECTED, &AuthSocketHandler::handleLogonProof },
    { CMD_AUTH_RECONNECT_CHALLENGE, STATUS_CONNECTED, &AuthSocketHandler::handleReconnectChallenge },
    { CMD_AUTH_RECONNECT_PROOF, STATUS_CONNECTED, &AuthSocketHandler::handleReconnectProof },
    { CMD_REALM_LIST, STATUS_AUTHED, &AuthSocketHandler::handleRealmList }
};
#define AUTH_TOTAL_COMMANDS sizeof(table)/sizeof(AuthHandler)

#define s_BYTE_SIZE 32


AuthSocketHandler::AuthSocketHandler(Shared::Net::Socket &peer)
    : SocketEventHandler(peer)

    , _authed(false)
    , _connTime(0)
    , _closing(false)
    , _packetProcessing(false)

    , _accountId(0)
    , _loginName()
    , _safeLoginName()
    , _build(0)
    , _localizationName()
    , _accountsecLv(Shared::SEC_PLAYER)

    , N(), s(), g(), v()
    , b(), B()
    , K()
    , _reconnectProof()
{
    N.setHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
    g.setUInt32(7);
}

AuthSocketHandler::~AuthSocketHandler(void)
{

}

void AuthSocketHandler::markClose(void)
{
    if (!_closing) {
        _closing = true;
        peer().enableRead(false);
    }
}

void AuthSocketHandler::checkDeadLink(time_t maxAuthTime)
{
    if (!_closing && !_authed && (maxAuthTime > 0)) {
        /* 验证超时 */
        ::time_t now;
        ::time(&now);
        if ((now - _connTime) >= (maxAuthTime / 1000)) {
            markClose();
            sharedLog.outDebug("AuthSocketHandler::checkDeadLink() %d connection auth timeout", peer().id());
        }
    }
}

bool AuthSocketHandler::tryClose(void)
{
    if (!_packetProcessing) {
        peer().close();
        return true;
    }
    return false;
}

void AuthSocketHandler::onOpen(void)
{
    ::time(&_connTime);

	sharedLog.outDebug("AuthSocketHandler::onOpen() %d connection opened from %s:%d", 
		peer().id(), peer().ip().c_str(), peer().port());

    realmdSystem.addUnauthedSocket(peer().id(), this);
}

void AuthSocketHandler::onRead(void)
{
    SHARED_ASSERT(!_closing);
    setPacketProcess(true);

    std::uint8_t cmd;
    while (true)
    {
        if (peer().receivePeek(&cmd, 1) != 1) {
            break;
        }

        ::std::size_t x;
        for (x = 0; x < AUTH_TOTAL_COMMANDS; ++x)
        {
            if (((::std::uint8_t)table[x].cmd == cmd) &&
                ((table[x].status == STATUS_CONNECTED) ||
                (_authed && table[x].status == STATUS_AUTHED)))
            {
                sharedLog.outDebug("AuthSocketHandler::onRead() %d got data for cmd %u recv length %d", 
                    peer().id(), (std::uint32_t)cmd, peer().receiveSize());

                if (!(*this.*table[x].handler)())
                {
                    sharedLog.outDebug("AuthSocketHandler::onRead() %d command handler failed for cmd %u recv length %d", 
                        peer().id(), (std::uint32_t)cmd, peer().receiveSize());
                    setPacketProcess(false);
                    return;
                }
                break;
            }
        }

        if (x == AUTH_TOTAL_COMMANDS)
        {
            sharedLog.outDebug("AuthSocketHandler::onRead() %d got unknown packet %u", 
                peer().id(), (std::uint32_t)cmd);
            markClose();
            break;
        }
    }

    setPacketProcess(false);
}

void AuthSocketHandler::onWrite(void)
{

}

void AuthSocketHandler::onEof(void)
{
	sharedLog.outDebug("AuthSocketHandler::onEof() %d connection EOF", peer().id());
	markClose();
}

void AuthSocketHandler::onClose(void)
{
    sharedLog.outDebug("AuthSocketHandler::onClose() %d connection closed",peer().id());
}

void AuthSocketHandler::onError(void)
{
	sharedLog.outDebug("AuthSocketHandler::onError() %d connection ERROR %d, %s", 
		peer().id(), peer().errorCode(), peer().errorString().c_str());
    markClose();
}

void AuthSocketHandler::onTimeout(int flags)
{

}

bool AuthSocketHandler::handleLogonChallenge(void)
{
    if (peer().receiveSize() < sizeof(sAuthLogonChallenge_C)) {
        return false;
    }

    /* 读包头 */
    std::vector<std::uint8_t> buf;
    buf.resize(4);
    peer().receive((std::uint8_t*)&buf[0], 4);
#ifdef SHARED_BIGENDIAN
    Util::endianConvert(*((std::uint16_t*)(buf[0])));
#endif

    std::uint16_t remaining = ((sAuthLogonChallenge_C*)&buf[0])->size;
    sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d got header, body is %#04x bytes", peer().id(), remaining);

    if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size())
        || (peer().receiveSize() < remaining)) {
            /* 数据包不完整 */
            markClose();
            return false;
    }

    buf.resize(remaining + buf.size() + 1);
    buf[buf.size() - 1] = 0;
    sAuthLogonChallenge_C* ch = (sAuthLogonChallenge_C*)&buf[0];

    /* 读取剩下的数据 */
    peer().receive((std::uint8_t*)&buf[4], remaining);
    sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d got full packet, %#04x bytes", peer().id(), ch->size);
    sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d name(%d): '%s'", peer().id(), ch->I_len, ch->I);

    ::LocalByteBuffer pkt;

    _loginName = (const char*)ch->I;
    _build = ch->build;
    _safeLoginName = _loginName;
    realmdSystem.loginDB().escapeString(_safeLoginName);

    pkt << (std::uint8_t) CMD_AUTH_LOGON_CHALLENGE;
    pkt << (std::uint8_t) 0x00;

    /* 检查IP地址是否被禁止登录 */
    std::string address = peer().ip();
    realmdSystem.loginDB().escapeString(address);
    QueryResultPtr result(realmdSystem.loginDB().pquery("SELECT unbandate FROM ip_banned WHERE "
        //    permanent                    still banned
        "(unbandate = bandate OR unbandate > UNIX_TIMESTAMP()) AND ip = '%s'", address.c_str()));
    if (result.get())
    {
        pkt << (std::uint8_t)WOW_FAIL_BANNED;
        sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d Banned ip %s tries to login!", 
            peer().id(), peer().ip().c_str());
    }
    else
    {
        result.reset(realmdSystem.loginDB().pquery(
            "SELECT sha_pass_hash,id,locked,last_ip,gmlevel,v,s FROM account WHERE username = '%s'", _safeLoginName.c_str()));
        if (result.get())
        {
            /* 检查帐号是否被锁定登录IP，如果被锁定检查当前登录IP是否是锁定值 */
            bool locked = false;
            if ((*result)[2].toUInt8() == 1)
            {
                sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d Account '%s' is locked to IP - '%s'", 
                    peer().id(), _loginName.c_str(), (*result)[3].c_str());
                sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d Player address is '%s'", peer().id(), peer().ip().c_str());
                if (strcmp((*result)[3].c_str(), peer().ip().c_str()))
                {
                    sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d Account IP differs", peer().id());
                    pkt << (std::uint8_t) WOW_FAIL_SUSPENDED;
                    locked = true;
                }
                else {
                    sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d Account IP matches", peer().id());
                }
            }
            else {
                sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d Account '%s' is not locked to ip", peer().id(), _loginName.c_str());
            }

            /* 帐号没有被锁定登录IP或当前登录IP等于锁定值 */
            if (!locked)
            {
                // 检查帐号是否被禁止登录
                QueryResultPtr banResult(realmdSystem.loginDB().pquery("SELECT bandate,unbandate FROM account_banned WHERE "
                    "id = %u AND active = 1 AND (unbandate > UNIX_TIMESTAMP() OR unbandate = bandate)", (*result)[1].toUInt32()));
                if (banResult.get())
                {
                    if ((*banResult)[0].toUInt64() == (*banResult)[1].toUInt64())
                    {
                        pkt << (std::uint8_t) WOW_FAIL_BANNED;
                        sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge()%d Banned account %s tries to login!", peer().id(), _loginName.c_str());
                    }
                    else
                    {
                        pkt << (std::uint8_t) WOW_FAIL_SUSPENDED;
                        sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d Temporarily banned account %s tries to login!", peer().id(), _loginName.c_str());
                    }
                }
                else
                {
                    _accountId = (*result)[1].toUInt32();

                    /* 帐号没有被禁止登录 */
                    std::string rI = (*result)[0].toString();

                    /* 从数据库读取v,s的值 */
                    std::string dbV = (*result)[5].toString();
                    std::string dbS = (*result)[6].toString();
                    sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d database authentication values: v='%s' s='%s'", peer().id(), dbV.c_str(), dbS.c_str());

                    /* 如果数据库没有保存v,s或者保存的v,s不符合要求则重新计算v,s */
                    if (dbV.size() != s_BYTE_SIZE * 2 || dbS.size() != s_BYTE_SIZE * 2) {
                        setVSFields(rI);
                    }
                    else {
                        s.setHexStr(dbS.c_str());
                        v.setHexStr(dbV.c_str());
                    }

                    /* 计算B */
                    b.setRand(19 * 8);
                    BigNumber gmod = g.modExp(b, N);
                    B = ((v * 3) + gmod) % N;

                    SHARED_ASSERT(gmod.getNumBytes() <= 32);

                    BigNumber unk3;
                    unk3.setRand(16 * 8);

                    pkt << (std::uint8_t)WOW_SUCCESS;
                    pkt.append(B.asByteArray(32), 32);
                    pkt << (std::uint8_t)1;
                    pkt.append(g.asByteArray(), 1);
                    pkt << (std::uint8_t)32;
                    pkt.append(N.asByteArray(32), 32);
                    pkt.append(s.asByteArray(), s.getNumBytes());
                    pkt.append(unk3.asByteArray(16), 16);

                    std::uint8_t securityFlags = 0;
                    pkt << (std::uint8_t)securityFlags;

                    if (securityFlags & 0x01)
                    {
                        pkt << std::uint32_t(0);
                        pkt << std::uint64_t(0) << std::uint64_t(0);
                    }

                    if (securityFlags & 0x02)
                    {
                        pkt << std::uint8_t(0);
                        pkt << std::uint8_t(0);
                        pkt << std::uint8_t(0);
                        pkt << std::uint8_t(0);
                        pkt << std::uint64_t(0);
                    }

                    if (securityFlags & 0x04)
                    {
                        pkt << std::uint8_t(1);
                    }

                    /* 解析帐号等级和地域 */
                    std::uint8_t secLevel = (*result)[4].toUInt8();
                    _accountsecLv = (secLevel <= SEC_ADMINISTRATOR ? eAccountTypes(secLevel) : SEC_ADMINISTRATOR);

                    _localizationName.resize(4);
                    for (int i = 0; i < 4; ++i) {
                        _localizationName[i] = ch->country[4 - i - 1];
                    }

                    sharedLog.outDebug("AuthSocketHandler::handleLogonChallenge() %d account %s is using '%c%c%c%c' locale (%u)", peer().id(),
                        _loginName.c_str(), ch->country[3], ch->country[2],
                        ch->country[1], ch->country[0], getLocaleByName(_localizationName));
                }
            }
        }
        else
        {
            /* 没有这个帐号 */
            pkt << (std::uint8_t) WOW_FAIL_UNKNOWN_ACCOUNT;
        }
    }

    peer().send((std::uint8_t*)pkt.contents(), pkt.size());
    return true;
}

bool AuthSocketHandler::handleLogonProof(void)
{
    if (peer().receiveSize() < sizeof(sAuthLogonProof_C)) {
        return false;
    }

    /* 读取数据包 */
    sAuthLogonProof_C lp;
    peer().receive((std::uint8_t*)&lp, sizeof(sAuthLogonProof_C));

    /* 目前只支持6141版本客户端 */
    if (!isAcceptableClientBuild(6141))
    {
        /* 客户端版本不匹配 */
        ::LocalByteBuffer pkt;
        pkt << (std::uint8_t) CMD_AUTH_LOGON_CHALLENGE;
        pkt << (std::uint8_t) 0x00;
        pkt << (std::uint8_t) WOW_FAIL_VERSION_INVALID;
        sharedLog.outDebug("AuthSocketHandler::handleLogonProof() %d %u is not a valid client version!", peer().id(), _build);
        peer().send((std::uint8_t*)pkt.contents(), pkt.size());
        return true;
    }

    //---- SRP6 验证 -----------------------------------------------------------
    BigNumber A;

    A.setBinary(lp.A, 32);

    // SRP 安全检查: A必须不等于0
    if (A.isZero()) {
        sharedLog.outError("AuthSocketHandler::handleLogonProof() %d SRP Check, A is zero", peer().id());
        markClose();
        return false;
    }

    Sha1Hash sha;
    sha.updateBigNumbers(&A, &B, NULL);
    sha.finalize();
    BigNumber u;
    u.setBinary(sha.digest(), 20);
    BigNumber S = (A * (v.modExp(u, N))).modExp(b, N);

    std::uint8_t t[32];
    std::uint8_t t1[16];
    std::uint8_t vK[40];
    memcpy(t, S.asByteArray(32), 32);
    for (int i = 0; i < 16; ++i) {
        t1[i] = t[i * 2];
    }
    sha.initialize();
    sha.updateData(t1, 16);
    sha.finalize();
    for (int i = 0; i < 20; ++i) {
        vK[i * 2] = sha.digest()[i];
    }
    for (int i = 0; i < 16; ++i) {
        t1[i] = t[i * 2 + 1];
    }
    sha.initialize();
    sha.updateData(t1, 16);
    sha.finalize();
    for (int i = 0; i < 20; ++i) {
        vK[i * 2 + 1] = sha.digest()[i];
    }
    K.setBinary(vK, 40);

    std::uint8_t hash[20];

    sha.initialize();
    sha.updateBigNumbers(&N, NULL);
    sha.finalize();
    memcpy(hash, sha.digest(), 20);
    sha.initialize();
    sha.updateBigNumbers(&g, NULL);
    sha.finalize();
    for (int i = 0; i < 20; ++i) {
        hash[i] ^= sha.digest()[i];
    }
    BigNumber t3;
    t3.setBinary(hash, 20);

    sha.initialize();
    sha.updateData(_loginName);
    sha.finalize();
    std::uint8_t t4[SHA_DIGEST_LENGTH];
    memcpy(t4, sha.digest(), SHA_DIGEST_LENGTH);

    sha.initialize();
    sha.updateBigNumbers(&t3, NULL);
    sha.updateData(t4, SHA_DIGEST_LENGTH);
    sha.updateBigNumbers(&s, &A, &B, &K, NULL);
    sha.finalize();
    BigNumber M;
    M.setBinary(sha.digest(), 20);

    if (!memcmp(M.asByteArray(), lp.M1, 20))
    {
        /* 验证成功 */

        sharedLog.outBasic("AuthSocketHandler::handleLogonProof() %d User '%s' successfully authenticated", peer().id(), _loginName.c_str());

        /* 更新帐号会话KEY，最后登录IP，最后登录时间，登录区域，失败登陆次数 */
        const char* K_hex = K.asHexStr();
        realmdSystem.loginDB().pexecute("UPDATE account SET "
            "sessionkey = '%s', "
            "last_ip = '%s', "
            "last_login = NOW(), "
            "locale = '%u', "
            "failed_logins = 0 WHERE username = '%s'",
            K_hex, peer().ip().c_str(), getLocaleByName(_localizationName), _safeLoginName.c_str());
        BigNumber::free((void*)K_hex);

        /* 向客户端发送SRP6验证数据 */
        sha.initialize();
        sha.updateBigNumbers(&A, &M, &K, NULL);
        sha.finalize();
        sendProof(sha);

        /* 设置为已通过验证 */
        _authed = true;
        realmdSystem.moveSocketAuthed(peer().id());
    }
    else {
        /* 验证失败 */
        std::uint8_t data[2] = { CMD_AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT };
        peer().send(data, sizeof(data));

        sharedLog.outDebug("AuthSocketHandler::handleLogonProof() %d account %s tried to login with wrong password", peer().id(), _loginName.c_str());

        int maxWrongPass = sharedAppConfig.getInt("REALMD", "MaxWrongPass", 0);
        if (maxWrongPass > 0)
        {
            realmdSystem.loginDB().pexecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '%s'", _safeLoginName.c_str());

            QueryResultPtr result(realmdSystem.loginDB().pquery("SELECT id, failed_logins FROM account WHERE username = '%s'", _safeLoginName.c_str()));
            if (result.get())
            {
                Field* fields = result->fetch();
                std::uint32_t failedLogins = fields[1].toUInt32();

                if (failedLogins >= (std::uint32_t)maxWrongPass)
                {
                    std::uint32_t wrongPassBanTime = sharedAppConfig.getInt("REALMD", "WrongPassBanTime", 600);
                    int wrongPassBanType = sharedAppConfig.getInt("REALMD", "WrongPassBanType", 0);

                    if (wrongPassBanType == 0)
                    {
                        std::uint32_t accId = fields[0].toUInt32();
                        realmdSystem.loginDB().pexecute("INSERT INTO account_banned VALUES ('%u',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','node realmd','Failed login autoban',1)",
                            accId, wrongPassBanTime);
                        sharedLog.outDebug("AuthSocketHandler::handleLogonProof() account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
                            _loginName.c_str(), wrongPassBanTime, failedLogins);
                    }
                    else
                    {
                        std::string currentIp = peer().ip();
                        realmdSystem.loginDB().escapeString(currentIp);
                        realmdSystem.loginDB().pexecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban')",
                            currentIp.c_str(), wrongPassBanTime);
                        sharedLog.outDebug("AuthSocketHandler::handleLogonProof() IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times",
                            currentIp.c_str(), wrongPassBanTime, _loginName.c_str(), failedLogins);
                    }
                }
            }
        }
    }

    return true;
}

bool AuthSocketHandler::handleReconnectChallenge(void)
{
    if (peer().receiveSize() < sizeof(sAuthLogonChallenge_C)) {
        return false;
    }

    /* 读包头 */
    std::vector<std::uint8_t> buf;
    buf.resize(4);
    peer().receive((std::uint8_t*)&buf[0], 4);

    ::std::uint16_t remaining = ((sAuthLogonChallenge_C*)&buf[0])->size;
    sharedLog.outDebug("AuthSocketHandler::handleReconnectChallenge() %d got header, body is %#04x bytes", peer().id(), remaining);

    if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size())
        || peer().receiveSize() < remaining) {
            /* 数据包不完整 */
            markClose();
            return false;
    }

    buf.resize(remaining + buf.size() + 1);
    buf[buf.size() - 1] = 0;
    sAuthLogonChallenge_C* ch = (sAuthLogonChallenge_C*)&buf[0];

    /* 读取剩余数据 */
    peer().receive((::std::uint8_t*)&buf[4], remaining);
    sharedLog.outDebug("AuthSocketHandler::handleReconnectChallenge() %d got full packet, %#04x bytes", peer().id(), ch->size);
    sharedLog.outDebug("AuthSocketHandler::handleReconnectChallenge() %d name(%d): '%s'", peer().id(), ch->I_len, ch->I);

    _loginName = (const char*)ch->I;
    _safeLoginName = _loginName;
    realmdSystem.loginDB().escapeString(_safeLoginName);
    _build = ch->build;

    QueryResultPtr result(realmdSystem.loginDB().pquery("SELECT sessionkey, id FROM account WHERE username = '%s'", 
        _safeLoginName.c_str()));

    /* 检查帐号是否存在 */
    if (!result.get())
    {
        sharedLog.outError("AuthSocketHandler::handleReconnectChallenge() %d user %s tried to login and we can not find his session key in the database.", peer().id(), _loginName.c_str());
        markClose();
        return false;
    }

    _accountId = (*result)[1].toUInt32();
    K.setHexStr((*result)[0].c_str());

    /* 发送响应数据 */
    ::LocalByteBuffer pkt;
    pkt << (std::uint8_t)  CMD_AUTH_RECONNECT_CHALLENGE;
    pkt << (std::uint8_t)  0x00;
    _reconnectProof.setRand(16 * 8);
    pkt.append(_reconnectProof.asByteArray(16), 16);
    pkt << (std::uint64_t) 0x00 << (::std::uint64_t) 0x00;
    peer().send((std::uint8_t*)pkt.contents(), pkt.size());
    return true;
}

bool AuthSocketHandler::handleReconnectProof(void)
{
    if (peer().receiveSize() < sizeof(sAuthReconnectProof_C)) {
        return false;
    }

    /* 读取数据包 */
    sAuthReconnectProof_C lp;
    peer().receive((std::uint8_t*)&lp, sizeof(sAuthReconnectProof_C));

    if (_loginName.empty() || !_reconnectProof.getNumBytes() || !K.getNumBytes()) {
        sharedLog.outError("AuthSocketHandler::handleReconnectProof() %d Check loginname "
            "or reconnectproof or session key failed", peer().id());
        markClose();
        return false;
    }

    BigNumber t1;
    t1.setBinary(lp.R1, 16);

    Sha1Hash sha;
    sha.initialize();
    sha.updateData(_loginName);
    sha.updateBigNumbers(&t1, &_reconnectProof, &K, NULL);
    sha.finalize();

    if (!memcmp(sha.digest(), lp.R2, SHA_DIGEST_LENGTH))
    {
        /* 发送响应 */
        ::LocalByteBuffer pkt;
        pkt << (std::uint8_t)  CMD_AUTH_RECONNECT_PROOF;
        pkt << (std::uint8_t)  0x00;
        peer().send((std::uint8_t*)pkt.contents(), pkt.size());

        /* 设置为已通过验证 */
        _authed = true;
        realmdSystem.moveSocketAuthed(peer().id());
        return true;
    }
    else
    {
        /* 验证失败，关闭连接 */
        sharedLog.outError("AuthSocketHandler::handleReconnectProof() %d user %s tried to login, but session invalid.", peer().id(), _loginName.c_str());
        markClose();
        return false;
    }
}

bool AuthSocketHandler::handleRealmList(void)
{
    if (peer().receiveSize() < 5) {
        return false;
    }

    peer().receiveSkip(5);

    QueryResultPtr result(realmdSystem.loginDB().pquery(
        "SELECT id, sha_pass_hash FROM account WHERE username='%s'", _safeLoginName.c_str()));
    if (!result.get())
    {
        sharedLog.outError("AuthSocketHandler::handleRealmList() %d user %s tried to login and we can not find him in the database.", peer().id(), _loginName.c_str());
        markClose();
        return false;
    }

    std::uint32_t id = (*result)[0].toUInt32();
    std::string rI = (*result)[1].toString();

    /* 加载服务器列表 */
    Binary::ByteBuffer rmpkt;
    loadRealmlist(rmpkt, id);

    Binary::ByteBuffer pkt;
    pkt << (std::uint8_t)CMD_REALM_LIST;
    pkt << (std::uint16_t)rmpkt.size();
    pkt.append(rmpkt);

    peer().send((std::uint8_t*)pkt.contents(), pkt.size());
    return true;
}

void AuthSocketHandler::setVSFields(const std::string& rI)
{
    /* 计算v,s */ 
    s.setRand(s_BYTE_SIZE * 8);

    BigNumber I;
    I.setHexStr(rI.c_str());

    // In case of leading zeros in the rI hash, restore them
    std::uint8_t mDigest[SHA_DIGEST_LENGTH];
    memset(mDigest, 0, SHA_DIGEST_LENGTH);
    if (I.getNumBytes() <= SHA_DIGEST_LENGTH) {
        memcpy(mDigest, I.asByteArray(), I.getNumBytes());
    }

    std::reverse(mDigest, mDigest + SHA_DIGEST_LENGTH);

    Sha1Hash sha;
    sha.updateData(s.asByteArray(), s.getNumBytes());
    sha.updateData(mDigest, SHA_DIGEST_LENGTH);
    sha.finalize();
    BigNumber x;
    x.setBinary(sha.digest(), sha.length());
    v = g.modExp(x, N);

    // 把v,s保存到数据库
    const char* v_hex, *s_hex;
    v_hex = v.asHexStr();
    s_hex = s.asHexStr();
    realmdSystem.loginDB().pexecute("UPDATE account SET v = '%s', s = '%s' WHERE username = '%s'", 
        v_hex, s_hex, _safeLoginName.c_str());
    BigNumber::free((void*)v_hex);
    BigNumber::free((void*)s_hex);
}

void AuthSocketHandler::sendProof(Sha1Hash& sha)
{
    sAuthLogonProof_S_BUILD_6005 proof;
    memcpy(proof.M2, sha.digest(), 20);
    proof.cmd = CMD_AUTH_LOGON_PROOF;
    proof.error = 0;
    proof.unk2 = 0x00;

    peer().send((std::uint8_t*)&proof, sizeof(proof));
}

void AuthSocketHandler::loadRealmlist(Binary::ByteBuffer& pkt, std::uint32_t id)
{
    RealmList::RealmArray realms = realmdSystem.realmList().getRealms();

    pkt << std::uint32_t(0);
    pkt << std::uint8_t(realms.size());

    /* 将数据填充到数据包 */
    RealmList::RealmArray::iterator iter = realms.begin();
    for (; iter != realms.end(); ++iter)
    {
        QueryResultPtr result(realmdSystem.loginDB().pquery(
            "SELECT numchars FROM realmcharacters WHERE realmid = %d AND acctid = %d;",
            iter->id, id));

        std::uint8_t chars = 0;
        if (result.get()) {
            chars = (*result)[0].toUInt8();
        }

        pkt << std::uint32_t(iter->icon);            /* icon */
        pkt << std::uint8_t(iter->realmflags);       /* realm flag */
        pkt << iter->name;                           /* 服务器名 */
        pkt << iter->address;                        /* 服务器地址 */
        pkt << iter->populationLevel;                /* population */
        pkt << chars;                                /* 帐号在该服务器的角色数量 */
        pkt << iter->timezone;                       /* 时区 */
        pkt << std::uint8_t(0x00);                   /* unk */
    }

    pkt << std::uint16_t(0x0002);
}